package com.xch.sboot.service.user;

import com.xch.sboot.common.constant.CachePrefixContant;
import com.xch.sboot.common.exception.BusinessException;
import com.xch.sboot.common.generator.ULIDGenerator;
import com.xch.sboot.common.util.HmacSHA;
import com.xch.sboot.common.util.RSAUtil;
import com.xch.sboot.config.login.LoginProperties;
import com.xch.sboot.config.login.Session;
import com.xch.sboot.persistence.domain.account.User;
import com.xch.sboot.persistence.repository.account.UserRepository;
import com.xch.sboot.service.common.RedisDataService;
import com.xch.sboot.service.user.dto.SignInDTO;
import com.xch.sboot.service.user.dto.SignUpDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;

/**
 * @author xch
 * 2022/12/14 14:45
 */
@Service
@Slf4j
public class SignService {

    @Resource
    private RedisDataService redisDataService;

    @Resource
    private LoginProperties loginProperties;

    @Resource
    private UserRepository userRepository;

    public Session signin(SignInDTO signInDTO) {
        User user = userRepository.findFirstByUsernameOrEmailAndDelFlagIsFalse(signInDTO.getUsername(), signInDTO.getUsername());
        if (user == null || !StringUtils.hasText(user.getId())) {
            throw new BusinessException("用户名/邮箱或密码错误");
        }
        //加密串解密
        String passwordsSaltSign = getPasswordsSaltSign(signInDTO.getPasswordStr());
        if (user.getPassword().equals(passwordsSaltSign)) {
            Session session = getSession(user);
            log.info("sign in success session {}", session);
            return session;
        }
        throw new BusinessException("用户名/邮箱或密码错误!");
    }

    private Session getSession(User user) {
        String token = ULIDGenerator.ULID();
        Session session = new Session(user, token);
        redisDataService.setRedisData(CachePrefixContant.SIGNIN_TOKEN_PREFIX + token, session, loginProperties.getValidTime());
        return session;
    }

    public void signup(SignUpDTO signUpDTO) {
        String username = signUpDTO.getUsername();
        boolean existsByUsername = userRepository.existsByUsernameAndDelFlagIsFalse(username);
        if (existsByUsername) {
            throw new BusinessException("用户名已存在！");
        }
        String email = signUpDTO.getEmail();
        boolean existsByEmail = userRepository.existsByEmailAndDelFlagIsFalse(email);
        if (existsByEmail) {
            throw new BusinessException("邮箱已被注册！");
        }
        String nickname = signUpDTO.getNickname();
        boolean existsByNickname = userRepository.existsByNicknameAndDelFlagIsFalse(nickname);
        if (existsByNickname) {
            throw new BusinessException("昵称已被使用！");
        }

        String passwordsSaltSign = getPasswordsSaltSign(signUpDTO.getPasswordStr());
        User user = new User();
        String ulid = ULIDGenerator.ULID();
        user.setId(ulid);
        user.setUsername(username);
        user.setEmail(email);
        user.setNickname(nickname);
        user.setDelFlag(Boolean.FALSE);
        user.setPassword(passwordsSaltSign);
        user.setSalt(loginProperties.getSalt());
        userRepository.save(user);
        log.info("sign up success username {}",user.getUsername());
    }

    /**
     * 根据前端传输的密码加密串
     * ——>私钥解密——>前后加盐——>hmacSha加签
     * 获取到密码存储串
     * @param passwordStr 登录密码加密串
     * @return 返回密码存储串
     */
    private String getPasswordsSaltSign(String passwordStr) {
        String passwords;
        try {
            passwords = RSAUtil.decrypt(passwordStr, RSAUtil.getPrivateKey(loginProperties.getPrivateKey()));
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("RSA 解密失败" + e.getMessage());
        }
        String passwordsSalt = loginProperties.getSalt() + passwords + loginProperties.getSalt();
        return HmacSHA.signString(passwordsSalt, loginProperties.getHmacKey());
    }

    /**
     * 删除缓存token-登出
     * @param token token
     */
    public void signout(String token) {
        redisDataService.delete(CachePrefixContant.SIGNIN_TOKEN_PREFIX + token);
    }
}
